﻿/*
 * DATAFLOWCORE
 * 
Copyright 2012 - Mindstorm Multitouch Limited

Author - Bertrand Nouvel

DataFlowCore is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

DataFlowCore is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser Public License for more details.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DFlow
{
    public class Wire : NodeMonitor
    {
        public DFlow.Node srcnode;
        public DFlow.Node dstnode;
        public string srcpin;
        public string dstpin;

        int srcpinidx = -1;
        int dstpinidx = -1;

        bool conversion=false;
        Func<Object, Object, Object> converter;
        object previously_instantiated_object = null;


        public bool metadata_wire = false; // metadata wire are not saved
        public bool crossdataflow_wire
        {
            get { return srcnode.host_Dataflow != dstnode.host_Dataflow; }
        }


        public override string ToString()
        {
            return System.String.Format("{0}@{1}->{2}@{3}", srcpin, srcnode.name, dstpin, dstnode.name);
        }


        public Wire(Node sn, string sp, Node dn, string dp)
        {
            if (sp.Contains("["))
            {
                int si = sp.IndexOf('['); int ei = sp.IndexOf(']');
                srcpinidx = System.Int32.Parse(sp.Substring(si + 1, ei - (si+1)));
                sp = sp.Substring(0, si);
            }
            if (dp.Contains("["))
            {
                int si = dp.IndexOf('['); int ei = dp.IndexOf(']');
                dstpinidx = System.Int32.Parse(dp.Substring(si + 1, ei - (si + 1)));
                dp = dp.Substring(0, si);
            }


            srcnode = sn; dstnode = dn; srcpin = sp; dstpin = dp;
            Type ost = sn.GetInputType(srcpin);
            Type odt = dn.GetOutputType(dstpin);
            Type st=ost;
            Type dt=odt;



            if (srcpinidx != -1)
            {
                System.Diagnostics.Debug.Assert(st.IsArray);
                st = st.GetElementType();
            }
            if (dstpinidx != -1)
            {
                System.Diagnostics.Debug.Assert(dt.IsArray);
                dt = dt.GetElementType();
            }


            
            // IS A CONVERSION NECESSARY ??
            if (st.FullName != dt.FullName)
            {
                conversion = true;
                converter = Converters.GetConverter(st, dt);
                if (converter == null)
                {
                    System.Diagnostics.Debug.Print("Failed to find a conversion for the wire " + ToString());
                    enabled = false;
                }
            }


            if (srcpinidx != -1)
            {
                System.Reflection.MethodInfo gv = ost.GetMethod("GetValue", new Type[] { typeof(int) });
                object [] idxs= { srcpinidx};
                if (!conversion) {
                    conversion=true;
                    converter = (i, o) =>
                    {
                        return gv.Invoke(i, idxs);  
                      };
                }
                else {
                    Func<object,object,object> tconverter=converter;
                    converter=((i,o)=>tconverter(gv.Invoke(i,idxs),o));                    
                }
            }
            if (dstpinidx != -1)
            {
                System.Reflection.MethodInfo gv = odt.GetMethod("GetValue",new Type[] {typeof(int)});
                System.Reflection.MethodInfo sv = odt.GetMethod("SetValue", new Type[] { typeof(int),odt.GetElementType() });
                if (!conversion)
                {
                    conversion = true;
                    converter = (i, o) =>
                    {
                        sv.Invoke(o, new object[] { dstpinidx, i });
                        return o;
                    };
                }
                else
                {
                    Func<object, object, object> tconverter = converter;
                    converter = ((i, o) => 
                    {
                        sv.Invoke(o, new object[] { dstpinidx, converter(i,gv.Invoke(o,new object[]{dstpinidx})) });
                        return o;
                    });
                }
            }

        }


        public override void OnPreProcess()
        {
            if (previously_instantiated_object != null)
            {
                if ((previously_instantiated_object as IDisposable) != null)
                {
                    (previously_instantiated_object as IDisposable).Dispose();
                }
                previously_instantiated_object = null;
            }
        }


        protected static PinInfo ResolveField(Node n, string nn)
        {
            if (nn[0] == '*')
            {
                return new PinInfo( n.GetParameters().GetType().GetField(nn),n.GetParameters());
            }
            return new PinInfo( n.GetType().GetField(nn),n);
        }


        public override void OnPostProcess()
        {
            if (!conversion)
            {
//                dstnode.GetType().GetField(dstpin).SetValue(dstnode, srcnode.GetType().GetField(srcpin).GetValue(srcnode));
                PinInfo srcp = ResolveField(srcnode, srcpin);
                PinInfo dstp = ResolveField(dstnode, dstpin);
                dstp.SetValue(srcp.GetValue());
            }
            else
            {
                bool processed = false;

                if (converter != null)
                {
                    PinInfo srcp = ResolveField(srcnode, srcpin);
                    PinInfo dstp = ResolveField(dstnode, dstpin);
                    object o1 = srcp.GetValue();
                    object o2 = dstp.GetValue();
                    object r = previously_instantiated_object = converter(o1, o2);
                    if ((Object.ReferenceEquals(previously_instantiated_object, o1)) || (Object.ReferenceEquals(previously_instantiated_object, o2)))
                    {
                        previously_instantiated_object = null;
                    }
                    dstp.SetValue(r);                    
                    processed = true;
                }
                if (!processed)
                {
                    PinInfo srcp = ResolveField(srcnode, srcpin);
                    PinInfo dstp = ResolveField(dstnode, dstpin);
                    dstp.SetValue(srcp.GetValue());
                }
            }
            if (dstpin[0] == '*')
            {
                dstnode.UpdateParameters(null); // TODO: <- TO BE OPTIMIZED (DIRTY BITS)
            }
        }
    }

}
